package work.month07.day02;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.sql.Date;
import java.util.ArrayList;
import java.util.List;

/**
 * @date 2020/07/02
 * @author 王光浩
 * @introduction 将 Student 对象持久化到文件中
 */
public class StudentDaoFile implements StudentDao {

	private static final Charset CS = Charset.forName("UTF-8");

	private Path path = null;
	private DataInputStream dataInput = null;
	private DataOutputStream dataOutput = null;

	private StudentDaoFile(Path path) {
		this.path = path;
	}

	/*
	 * 初始化 DataInputStream 对象和 DataOutputStream 对象
	 */
	private void initDataInputStream() {
		InputStream in = null;
		if (dataInput == null) {
			try {
				in = Files.newInputStream(path);
				dataInput = new DataInputStream(in);
			} catch (IOException e) {
				close();
				throw new RuntimeException("初始化数据时异常", e);
			}
		}
	}

	/*
	 * 初始化 DataOutputStream 对象
	 */
	private void initDataOutputStream() {
		OutputStream out = null;
		if (dataOutput == null) {
			try {
				out = Files.newOutputStream(path, StandardOpenOption.WRITE, StandardOpenOption.APPEND);
				dataOutput = new DataOutputStream(out);
			} catch (IOException e) {
				close();
				throw new RuntimeException("初始化数据时异常", e);
			}
		}
	}
	
	/*
	 * 将文件中所有的 Student 获取，然后封装到一个 List 集合中
	 */
	private List<Student> getAllStudent() {
		initDataInputStream();
		byte[] name = null;
		byte[] gender = null;
		Date birthday = null;
		List<Student> ret = new ArrayList<Student>();
		Student s = null;

		try {
			while (dataInput.available() > 0) {
				s = new Student();
				// 设置 id
				s.setId(dataInput.readInt());
				// 设置 name
				int lenName = dataInput.readInt();
				name = new byte[lenName];
				dataInput.read(name);
				s.setName(new String(name, CS));
				// 设置 gender
				int lenGender = dataInput.readInt();
				gender = new byte[lenGender];
				dataInput.read(gender);
				s.setGender(new String(gender, CS));
				// 设置 birthday
				long value = dataInput.readLong();
				birthday = new Date(value);
				s.setBirthday(birthday);
				ret.add(s);
			}
		} catch (IOException e) {
			close();
			e.printStackTrace();
		} finally {
			close();
		}
		return ret;
	}
	
	/**
	 * 关闭资源
	 */
	private void close() {
		if (dataOutput != null)
			try {
				dataOutput.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		if (dataInput != null)
			try {
				dataInput.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
	}


// ==========================  public 方法   ================================

	/**
	 * 返回一个在指定文件中进行读写 Student 对象的 StudentDaoFile 对象
	 * @param path 存储数据的文件路径
	 * @return 返回一个 StudentDaoFile 对象
	 */
	public static StudentDaoFile getInstance(Path path) {
		if (path == null || !Files.isRegularFile(path))
			throw new IllegalArgumentException("path 参数异常");
		return new StudentDaoFile(path);
	}

	/**
	 * 将 Student s 写入到一个文件中
	 * 如果 s 为空，返回 false
	 */
	@Override
	public boolean save(Student s) {
		initDataOutputStream();
		// 1. 判断参数条件
		if (s == null)
			return false;
		// 2. 将数据写入到对应的文件中
		try {
			// 2.1 写入 id
			dataOutput.writeInt(s.getId());
			// 2.2 写入姓名
			byte[] name = s.getName().getBytes(CS); // 将姓名按照 UTF-8 进行编码
			int lenName = name.length;
			dataOutput.writeInt(lenName); // 写入数组的长度
			dataOutput.write(name); // 将字符串对应的编码写入
			// 2.3 写入性别
			byte[] gender = s.getGender().getBytes(CS);
			int lenGender = gender.length;
			dataOutput.writeInt(lenGender);
			dataOutput.write(gender);
			// 2.4 写入 Date
			long date = s.getBirthday().getTime();
			dataOutput.writeLong(date);
		} catch (IOException e) {
			close();
			throw new IllegalArgumentException("path 参数异常", e);
		} finally {
			close();
		}
		return true;
	}

	/**
	 * 从文件中读取一个指定的 Student 对象
	 */
	@Override
	public Student find(Integer id) {
		if (id == null)
			throw new IllegalArgumentException("id 参数异常");
		List<Student> list = getAllStudent();
		for (Student s : list) {
			if (id.equals(s.getId()))
				return s;
		}
		return null;
	}
	
}
